package org.theqcp.wow.sim.engine.revised.scripts;

import java.io.File;
import java.io.IOException;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.python.core.PyException;
import org.python.util.PythonInterpreter;
import org.theqcp.wow.sim.engine.revised.Simulation;
import org.theqcp.wow.sim.engine.revised.enums.SimulationEventType;

import bsh.EvalError;
import bsh.Interpreter;

@SuppressWarnings( "unchecked" )
public abstract class ScriptedEventObserverFactory<EO extends SimulationEventObserver> {

    protected final Logger log = LogManager.getLogger(getClass().getSimpleName());

    private PythonInterpreter pythonInterpreter = null;
    private Interpreter bshInterpreter = null;

    public EO newEventObserver( SimulationEventType type, String basePath ) throws ClassNotFoundException {
	return newEventObserver(type.toString(), basePath);
    }

    public EO newEventObserver( String type, String basePath ) throws ClassNotFoundException {
	String pythonPath, javaPath;

	pythonPath = basePath + type + ".py";
	javaPath = basePath + type + ".java";

	if( new File(javaPath).exists() ) {
	    return newJavaEventObserver(type, javaPath);

	} else if( new File(pythonPath).exists() ) {
	    return newPythonEventObserver(type, pythonPath);

	} else {
	    String classPath = Simulation.class.getPackage() + ".imbues." + type;

	    try {
		SimulationEventObserver s = (SimulationEventObserver) Class.forName(classPath).newInstance();
		return (EO) s;

	    } catch( Exception e ) {
		log.error(e);
		log.debug(e, e);
		throw new ClassNotFoundException("Unable to find class for " + type, e);
	    }
	}
    }

    protected EO newJavaEventObserver( String type, String path ) {
	if( bshInterpreter == null ) {
	    bshInterpreter = new Interpreter();
	}

	EO eventObserver = null;

	try {
	    bshInterpreter.source(path);
	    SimulationEventObserver s = (SimulationEventObserver) bshInterpreter.eval("new " + type + "()");
	    eventObserver = (EO) s;

	} catch( ClassCastException e ) {
	    log.error(e);
	    log.debug(e, e);

	} catch( EvalError e ) {
	    log.error(e);
	    log.debug(e, e);

	} catch( IOException e ) {
	    log.error(e);
	    log.debug(e, e);
	}

	return eventObserver;
    }

    protected EO newPythonEventObserver( String type, String path ) {
	if( pythonInterpreter == null ) {
	    pythonInterpreter = new PythonInterpreter();
	}

	EO eventObserver = null;

	try {
	    pythonInterpreter.execfile(path);
	    SimulationEventObserver s = (SimulationEventObserver) pythonInterpreter.eval(type + "()").__tojava__(
		    Imbue.class);
	    eventObserver = (EO) s;

	} catch( PyException e ) {
	    log.error(e);
	    log.debug(e, e);
	}

	return eventObserver;
    }

}
